الوضع: في التطوير كجزء من lucidRAG. المصدر: github.comM SK1scottgal/lucidrag
أين يلائم هذا: VideoSummarizer هو الأوركسترا من lucidRAG العائلة, تدمج ثلاثة أنابيب في محرك تحليل فيديو موحد:
كلهم يتبعون نفس الشيء تخفيض نمط RAG: نستخرج الإشارات مرة واحدة , نحفظ الأدلة , ن синтезها مع مدخل LLM محدود
معالجة شريحة فيلم ساعتيّة بـ- بـ - بــ - بداخلات "CLIP" ستستغرق ساعات وتكلف مئات الدولارات في الحساب.
VideoSummarizer يحل هذا بثلاثة تحسينات رئيسية:
وكانت النتيجة : 2- ساعة من عملية الفيلم في ~10-15 دقائق , وليس ساعات S. نفس مبادئ العمارة كما ImageSummarizer و AudioSummarizer, لكن يتكون من أنابيب تحليل فيديو موحدة.
رؤية أساسية: الفيديو هو اللقطات + الصوت + النص. معالجة كل مجال باستخدام أدوات مخصصة
- بنية العملية أولاً (cutsM SK1 I-frames , audio segments)
- استخرج إشارات متقاطعة - مدوئية مرة واحدة (
المصطلحات:
(start_time, end_time) + إشارات + اشارات + provenanceنماذج ML الرئيسية المستخدمة:
هذا المقال يغطي
المقالات المترابطة:
المؤشرات القياسية: الأرقام التي تم قياسها على AMD 9950X (16-core) МSK4 NVIDIA AM SK5 | | (16 | GB |) |
الفيلم العادي يحتوي على:
المقاربة القشية ( لا أحد يفعل هذا , لكنه يحدد المقياس МSK2
حتى مع استخراج إطارات المفاتيح (sayM SK1 500-1000 frames), ذلكMSC4s لا يزال |100-200 ثوان من استنتاج السلسلة CLIP | .
النهج التقليدي: "إستخراج إطارات المفاتيح, أرسل إلى Vision LLM
المشكلة: هذا يحرق الحسابات على الإطارات المتفرطة (العديد من إطارات المفاتيح متشابهة بصريا), يعالجها بسلسلة ♫( معالجة GPU فارغة بين الإطارات ♫
الحل: متعددة- التصفية المراحلM SK2 معالجة العينات , وكون أسلاك الأنبوب
تطبيقات VideoSummarizer تقلل من RAG للفيديو بثلاثة مراحل
flowchart TB
subgraph Input["Video File (.mp4, .mkv, etc.)"]
V[Video Stream]
A[Audio Stream]
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]
end
subgraph Stage2["Stage 2: Content Extraction"]
IS[ImageSummarizer<br/>CLIP, OCR, Vision]
AS[AudioSummarizer<br/>Whisper, Diarization]
NER[NER Service<br/>Entity extraction]
end
subgraph Stage3["Stage 3: Scene Assembly"]
SC[SceneClusteringWave<br/>CLIP similarity]
EV[EvidenceGenerationWave<br/>RAG chunks]
end
V --> N --> SD --> KE
KE --> IS
A --> AS
AS --> NER
IS --> SC
NER --> SC
SC --> EV
style Stage1 stroke:#22c55e,stroke-width:2px
style Stage2 stroke:#3b82f6,stroke-width:2px
style Stage3 stroke:#8b5cf6,stroke-width:2px
قبل أن تذهب إلى التنفيذ , هنا ' هذا ما تحصل عليهschema المخرجي
| الأثر | الحقول الرئيسية | المصدر S |
|---|---|---|
| مشهد | id, start_time, end_time, key_terms[], speaker_ids[], embedding[512] |
SceneClusteringWave |
| إطلاق النار | id, start_time, end_time, cut_type, keyframe_path |
موجة إكتشاف الرصاص |
| التصريح | id, text, start_time, end_time, speaker_id, confidence |
موجة الترجمة |
| نغمة نصية | id, text, start_time, text_type (titleM SK1credit/subtitle /ocrMSC4 |
SubtitleExtractionWave |
| إطار مفاتيح | id, timestamp, frame_path, dhash, clip_embedding[512] |
إطار مفاتيحExtractionWave |
كل قطعة فنية تتضمن الموطن: موجة المصدرM SK1 علامة زمنية معالجة, درجة الثقةMSC3 هذا هو " مذكرة الدليلMNK5 التي تعمل على مستودعات RAG في الأسفل
VideoSummarizer يستخدم بنية موجية مبنية على إشارة حيث كل موجه يعلن عن إتفاقاته الإشارات بشكل واضح:
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; }
}
هذا يُمكن توازن الموجات الديناميكية:
تم تنفيذ استخراج الإطارات الرئيسية كـ 7 موجات رقيقة لتحسين التوازن والكفاءة في الحواسيب.
| الموجة | الأولوية | تتطلب | الإنبثاق | الوقت |
|---|---|---|---|---|
| NormalizeWave | 1000 | - | video.duration, video.fps, video.normalized |
~2s |
| موجة إكتشاف FFmpegShot | 900 | video.normalized |
shots.detected, shots.count |
~5-10s |
| موجة إكتشاف IFrame | 850 | video.normalized |
keyframes.iframes_detected, keyframes.iframes_count |
~3s |
| موجة إختيار إطار مفاتيح | 840 | shots.detected, keyframes.iframes_detected |
keyframes.selected, keyframes.selected_count |
~1s |
| موجة استtraukة لقطة | 830 | keyframes.selected |
keyframes.thumbnails_extracted |
~5s |
| موجة تفكيك إطار مفاتيح | 820 | keyframes.thumbnails_extracted |
keyframes.deduplicated, keyframes.duplicates_skipped |
~1s |
| لوحة مفاتيح FullResExtractionWave | 810 | keyframes.deduplicated |
keyframes.extracted, keyframes.count |
~10s |
| موجة الدمج الملتقط | 800 | keyframes.extracted |
clip.embeddings_ready, clip.embeddings_count |
~30s |
| موجة تحليل الصورة | 790 | keyframes.deduplicated |
keyframes.analyzed, ocr.extracted |
~60s |
| TitleCreditsDetectionWave | 750 | shots.detected |
title.detected, credits.detected |
~5s |
| موجة الاستخراج الصوتي | 650 | video.normalized |
audio.extracted, audio.path |
~30s |
| موجة الترجمة | 600 | audio.extracted |
transcription.complete, transcription.utterance_count |
~120s |
| موجة استtractionsubtitle | 550 | video.normalized |
subtitles.extracted |
~2s |
| فصل موجة الاستخراج | 500 | video.normalized |
chapters.extracted |
~1s |
| موجة الـ SceneClustering | 400 | shots.detected |
scenes.detected, scene.count |
~5s |
| موجة الأدلة | 100 | scenes.detected |
evidence.generated |
~2s |
ملاحظات:
keyframes.deduplicated (لا كاملاً-resM SK2 يعمل التصوير بالرسوم المتحركة على شاشات اللقطة ; يستعمل تعليق الرؤية الكاملةMSC4res عندما يكون متاحاً عن طريق التوجيه للقدرة | .الكلي لـ 2- ساعة من الفيلم: ~10-15 دقائق (vs. ساعات دون تحسين
الإشارات تُعرّف كثوابت للثبات:
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";
}
VideoSummarizer يستخدم القدرة-بنية معمارية: إكتشاف GPU مرة واحدة عند البداية, تحميل النماذج ببطءM SK2 عمل طريق إلى المكونات المتاحةMSC3
النماذج تم تعريفها في models.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]
// 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)
});
قيود السرعة , تخمين زمني , والضغط الخلفي التكيفي يحافظ على إستجابة الواجهة بينما يزيد من الإنتاجية
// 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
وثائق نظام القدرات الكاملة: أنظر
Mostlylucid.Summarizer.Core/Capabilities/للكشف عن GPU , ناقل إشارات / فرعي , , متحكمات الضغط الخلفي . , وتصميم بنية الشبكة
قبل تشغيل مداخلات CLIP باهظة الثمن, VideoSummarizer يفرز إطارات شبيهة بصريا باستخدام الاختلاف hash (dHash).
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);
}
مخرج مثالي:
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
لماذا يهم هذا؟
بدلاً من معالجة صورة واحدة في وقت واحد
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,
int batchSize = DefaultBatchSize,
CancellationToken ct = default)
{
var session = await GetOrLoadClipModelAsync(ct);
var results = new Dictionary<int, float[]>();
// Pre-index batch for O(1) lookup (not batch.IndexOf!)
var batches = framePaths
.Select((kvp, idx) => (idx, kvp.Key, kvp.Value))
.Chunk(batchSize);
foreach (var batch in batches)
{
// Create batch tensor [batchSize, 3, 224, 224]
var tensor = new DenseTensor<float>(new[] { batch.Length, 3, ClipImageSize, ClipImageSize });
// Preprocess images in parallel (simplified; production uses vectorised span copy)
Parallel.ForEach(batch, item =>
{
var (batchIdx, frameIndex, path) = item;
var localIdx = batchIdx % batchSize;
PreprocessImageToTensor(path, tensor, localIdx); // ImageSharp pixel buffers
});
// Single GPU pass for entire batch
var inputs = new List<NamedOnnxValue>
{
NamedOnnxValue.CreateFromTensor("input", tensor)
};
using var outputResults = session.Run(inputs);
// Extract embeddings from batch output...
}
return results;
}
}
مقارنة بالأداء:
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
لماذا ينجح معالجة العينات:
[8, 3, 224, 224] يستخدم نفس ذاكرة GPU كالصورة الواحدة ( أغلب)VideoSummarizer doesn't reinvent ImageSommarizer or AudioSum marizerit السلاسل هم.
تم تقسيم إستخراج الإطارات الرئيسية إلى موجات 7 جزيئية ( أنظر إلى جدول الموجات في الأعلى | ). | هنا |' | هذا هو نمط التسلسل الذي يظهر كيف يربطون معاً
// IFrameDetectionWave → KeyframeSelectionWave → ThumbnailExtractionWave
// → KeyframeDeduplicationWave → KeyframeFullResExtractionWave → ClipEmbeddingWave
// ClipEmbeddingWave coordinates with ImageSummarizer
public class ClipEmbeddingWave : IVideoWave, ISignalAwareVideoWave
{
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 (3-5x faster than serial)
var embeddings = await _batchClipService.GenerateBatchEmbeddingsAsync(
keyframes, 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);
}
}
}
الاستخراج الصوتي والتحويل الصوتي هي الآن إشارات منفصلة
// 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());
}
}
}
}
VideoSummarizer يستخرج كيانات مكتوبة باسمها من transcripts باستخدام BERT-based NER M SK1ONNX).
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();
}
}
مخرج مثالي:
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"]
لماذا يهم NER في الفيديو:
استخدام مداخلات CLIP وحدها للكشف عن المشهد لا ينجح ' القضبان الضئيلة من ناحية التصميم ( واحدة لكل لقطة تغيير | ), | لكن اللقطات كثيفة |. | مع | | مداخلة | لـ | لقطة | ومداخلات | 6 | تغطية | و | 7 | تجمع مداخلي نقي ينتج فقط | ما | 8 | مشهد | movie | ساعت |
VideoSummarizer يستخدم مقاربة إشارات متعددة - الذي يجمع إشارات 4 مع وزنها للكشف القوي عن الحدود في المشهد
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;
}
}
أقرب -نشر مركبة جيران: فقط ~2% من اللقطات لديها مداخلات CLIP مباشرة. المقاربة الجديدة تنشر المداخلات لطلقات قريبة خلال 30 ثواني باستخدام وزن القرب الزمنيM SK4
النوافذ الدلالية للترجمة: يبني 10- نافذة كلمة ثانية حول كل لقطة ويكشف التنقلات الدلالية من خلال المسافة عبر جاككارد بروكسة الهبوط الدلالي الرخيص | ( | تقليل النسخ |= | تغيير الموضوع | МSK4 | BM | 25 | يمكن استخدام النسخ أو الهبوط المدمج عندما يكون موجودا |
إدراك نوع القطع: الخفاء-toM SK2 التحولات السوداء والانحلال تشير بقوة إلى الحدود في المشهد , ترفع النتيجة الحدودية .
الحد المتكيف: عوضاً عن حد محدد, يختار الحدود من الأعلى 25% من الدرجات
القيود المؤقتة: يفرض الحد الأدنى 15 مشاهدته ويقوض حدوده في 5- अधिकतम دقيقة .
مثال:
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"
VideoSummarizer يمد إتصال الإشارات من ImageSumMarizer و AudioSum marizer:
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";
}
الإشارات الرئيسية المنبعثة
| إشارة | المصدر | |
|---|---|---|
video.duration |
أعادة موجة | مدة كاملة في ثواني |
video.resolution |
إحداث موجة طبيعية | عرض× طول |
video.fps |
موجة عادية | معدل الإطار |
shots.count |
موجة إكتشاف الرصاص | عدد الرصاصات التي تم إكتظافها |
keyframes.count |
لوحة مفاتيح موجة استخلاص | لوحات مفاتح فريدة بعد التراجع |
keyframes.duplicates_skipped |
KeyframeExtractionWave | Frames filtered by dHash |
scene.count |
موجة الـ SceneClustering | |
transcript.entities.per |
موجة الترجمة | أسماء الأشخاص من النطاق الشمالي للطاقة النووية |
transcript.entities.org |
موجة الترجمة | أسماء المنظمات |
transcript.word_count |
موجة التحويل | كل الكلمات في التحويل |
الـ VideoPipeline يحول إشارات الفيديو إلى ContentChunk لمؤشر RAG:
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);
}
}
مخرج مثالي للفيلم:
{
"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"
}
}
]
}
| المرحلة | الوقت | ملاحظات | |||||
|---|---|---|---|---|---|---|---|
| بيانات ملموسة FF | ~2s | ||||||
| إكتشاف الرصاص | MSC2s MSC3 فلتر مشهد FFmpeg S | ||||||
| إستخراج إطارات مفاتيح | ~30s | 500 | I | - | frames | ||
| تفكيك دوتة "dHash" | MSC2s MSC3 \MSC4 | MSC5 | MSC6 | إطارات | msc7 | ||
| مدمجة بطاقة CLIP | |||||||
| ImageSummarizer OCR | |||||||
| إستخراج الصوت | MSC2s MSC3 FFmpeg msc4 | ||||||
| ترجمة صامتة | ~180s | 2 | ساعات الكلام | ||||
| التلوث الصوتي | ~60s S | ECAPA-TDNN | |||||
| استخراج النيترونات | ~10s | BERT | - | النيرونات على الطباعة | |||
| تجميع المشهد | ~5s S | | | |||||
| مولد الأدلة | ~2s SSK3 | ||||||
| كل | ~8-10 دقائق |
| تحسين | توفير | ||||
|---|---|---|---|---|---|
| تفكيك دوتنه dHash | ~40% إطارات تلقح МSK3 ♫~24s حفظ الاقتران ♫ | ||||
| توصيل بطاقة | 3-5x أسرع = | ~180 | حفظت | ||
| تركيب أنابيب | استخدام ImageSummarizer | ||||
| الادخار الكلي | ~3-4 دقائق |
| مكون | الذاكرة |
|---|---|
| CLIP ViT | |
| قاعدة الشم | MSC2MB MSC3 |
| ECAPA-TDNN | |
| BERT-NER | ~500MB |
| القمة | ~1.5GB |
يسجل الفيديو سوممارزر كـ IPipeline للتوجيهات الآلية:
// 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");
التوسعات المدعمة:
.mp4, .mkv, .avi, .mov, .wmv, .webm, .flv, .m4v, .mpeg, .mpgويعرض الفيديو سوممارزر أن تركيب خطوط الأنابيب الدرجات
والنتيجة : فيلم ساعتي 2- يتحول إلى مسجل إشارات بنائي مع مشاهد ,, , Transcripts , , , Entities ., , and embeddingsready for RAG queries like .
نمط RAG المنخفض لفيديو:
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)
نظام القدرات:
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
هذا هو الغموض المقيد على المقياس:
الـ LLM يعمل على ما قبل - "الدلائل الحاسوبية" وليس الفيديو الخام"
الأنماط الأساسية:
تطبيقات RAG المنخفضة
| جزء | نمط | تركيز |
|---|---|---|
| 1 | الغموض المقيد | عنصر واحد |
| 2 | MoM مقيدة | أجزاء متعددة |
| 3 | سحب السياق | الوقت / الذاكرة |
| 4 | ذكاء الصورة | فن العمارة الموجية, 22 موجات |
| 4.1 | خط ثلاثي -Tier OCR | OCRM SK1 نماذج ONNX, شرائح الأفلام |
| 4.2 | AudioSummarizer | الصوت القضائي, تشلل السماعة |
| 4.3 | VideoSummarizer (this article) | الأوركستريشن التلفزيوني, بستة CLIP, NER |
التالي: متعددة-رسم بياني مدوئي RAG مع lucidRAG يجمع كل الأربعة ملخصات إلى رسم بياني علمي موحد مع متقاطعةM SK2ربط الكيان المدوئي .
كل الأجزاء تتبع نفس المتغير: مكونات الإحتمالية تقترح ; أن الأنظمة الثابتة تستمر.
© 2026 Scott Galloway — Unlicense — All content and source code on this site is free to use, copy, modify, and sell.